home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc Development Framework / Documentation / Engineering Notes / Views / Layout Management < prev    next >
Encoding:
Text File  |  1996-04-26  |  19.5 KB  |  354 lines  |  [TEXT/ttxt]

  1. OpenDoc
  2. Development
  3. Framework
  4.                                                                                                                                                                                             
  5. Layout Management 
  6. ODF Release 1                                                                                                                                                                     
  7.  
  8. This document explains how ODF helps you manage the layout of your views when the frame is resized and how you can customize the default behaviors.  This release provides a basic layout manager that handles  simple cases automatically and provides virtual methods to override for other cases.
  9.  
  10.  
  11. Table of Contents
  12. ----------------------
  13. • View Bindings
  14.     • Special Case of Frames
  15.     • Special Values for Each Type of View
  16. • Customizing the Layout of Views
  17.     • Example with Sibling Views
  18.     • Controling Maximum and Minimum Sizes
  19. • Layout of Views in Resources
  20. • API Reference
  21.  
  22.  
  23. View Bindings
  24.  
  25. By default each instance of FW_CView is layed out according to its fBounds and fBindings attributes.  The fBounds rectangle gives the position and size inside the parent view's content space.   The flag fBindings  represents its geometrical constraints, i.e. how the view's bounds should change when the extent of its parent view changes.
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45. A view's fBindings is the combination of 6 bits which can be turned on or off using the constants defined in FWViews.k:
  46.  
  47.     #define FW_kNoBinding            0x00000000
  48.     #define FW_kLeftBinding          0x00000001
  49.     #define FW_kRightBinding         0x00000002
  50.     #define FW_kTopBinding           0x00000004
  51.     #define FW_kBottomBinding     0x00000008
  52.     #define FW_kFixedWidth           0x00000010
  53.     #define FW_kFixedHeight          0x00000020
  54.  
  55. The Left/Right/Top/Bottom bits are on when the view  stays at a fixed distance to the left/right/top/bottom edge of the parent view content.  The Width and Height bits are on when the view's width or height is fixed (all horizontal constraints cannot be on at the same time and all vertical constraints cannot be on at the same time).
  56.  
  57. Notes:
  58.  
  59. • View bindings don't have any effect when changing the size or the location of this view  with SetSize or SetLocation.  They only apply to changes to the parent view.
  60.  
  61. • Changing the parent view location doesn't modify its subviews, only a change in the content size has any effect.  
  62.  
  63. • When the parent view doesn't have any scrolling content, i.e. its extent is the same as its size, the picture above shows the actual parent view bounds.  But if the parent view is a scrolling content view its visible bounds may be smaller in this picture.
  64.  
  65. Special Case of Frames
  66.  
  67. A frame is the root view of the hierarchy, the only view whose parent view is null.  Its fBindings attribute is not used.  
  68.  
  69. An embedded frame is not resized automatically by ODF when the size of its container changes, it is your responsibility. However you should never call SetSize directly on instances of FW_CFrame (FW_CFrame calls it internally in FrameShapeChanged).  In order to control the size of your frame use FW_MProxy::ChangeFrameShapes or the OpenDoc method ODFrame::ChangeFrameShape.
  70.  
  71. Similarily, you are not allowed to call SetLocation for a frame.
  72.  
  73. Default Values for Each Type of View
  74.  
  75. FWViews.k defines the following additional constants for your convenience:
  76.  
  77.     #define FW_kFixedLocation            (FW_kLeftBinding + FW_kTopBinding)
  78.     #define FW_kFixedSize                   (FW_kFixedWidth + FW_kFixedHeight)
  79.     #define FW_kFixedBounds                 (FW_kFixedLocation + FW_kFixedSize)
  80.     #define FW_kFitToEnclosure           (FW_kFixedLocation +     \
  81.                                                       FW_kRightBinding +     \ 
  82.                                                          FW_kBottomBinding)
  83.  
  84.     #define FW_kGrowBoxBinding           (FW_kBottomBinding +    \
  85.                                                          FW_kRightBinding +    \
  86.                                                          FW_kFixedSize)
  87.     #define FW_kVScrollBarBinding  (FW_kTopBinding +        \
  88.                                                       FW_kRightBinding +    \
  89.                                                       FW_kFixedWidth +        \
  90.                                                       FW_kBottomBinding)
  91.     #define FW_kHScrollBarBinding  (FW_kLeftBinding +     \
  92.                                                       FW_kBottomBinding +    \
  93.                                                       FW_kFixedHeight +        \
  94.                                                       FW_kRightBinding)
  95.  
  96. The constructor of FW_CView sets the default bindings to FW_kFixedBounds, i.e. most views should keep a fixed location and size when their parent is resized.  The same default is kept in all subclasses of FW_CView except FW_CSuperView, FW_CScrollBar and FW_CGrowBox.   Of course the correct behavior depends on your part's content, you can change the bindings by calling FW_CView::SetBindings. 
  97.  
  98. The constructor of FW_CSuperView sets the default bindings to FW_kFitToEnclosure: this is generally what you need for a content view.  The constructor of FW_CScrollBar uses FW_kHScrollBarBinding and FW_kVScrollBarBinding.  The constructor of FW_CGrowBox uses FW_kGrowBoxBinding so that the grow box remains at the bottom right corner of the frame.
  99.  
  100. When declaring views in resources you must explicitely set the bindings value you want in the corresponding view resource type.  For instance, to make an edit view grow horizontally with its parent use the value FW_kLeftBinding + FW_kRightBinding + FW_kTopBinding + FW_kFixedHeight.
  101.  
  102. Important: 
  103.  
  104. • ODF view bindings are not enough to handle the layout of sibling views relative to each other.  You must customize the layout manager as explained in the next section.
  105.  
  106. • A  value of 0, i.e. no bindings at all, should be a rare case.  It means that the view size and  location will change proportionally with the parent's extent.
  107.  
  108. • Be careful when changing the default bindings for views deriving from FW_CControl.  Buttons and popup menus should generally keep a fixed size.  Scroll bars must keep their standard  width in order to be displayed correctly.
  109.  
  110.  
  111. Customizing the Layout of Views
  112.  
  113. When the constraints defined by the view bindings are not enough to handle your layout you can override the  AdjustToNewLayout method of each view that requires it:
  114.  
  115. virtual void    AdjustToNewLayout(Environment *ev,  
  116.                                                                              const FW_CPoint& oldExtent, 
  117.                                                                              const FW_CPoint& newExtent, 
  118.                                                                              FW_Boolean refresh);
  119.  
  120. oldExtent = previous extent of the parent view.
  121. oldExtent = new extent of the parent view, to which the view must adjust.
  122. refresh = true if the modified regions should be updated.
  123.  
  124. This method is called by the parent view's SetExtent method since it where the change occurs.  It is important to note that AdjustToNewLayout is not called directly by SetSize because what matters is the size of the view content (where subviews are layed out), not the size of the view bounds (i.e. its visible portion).  However, SetSize ends up adjusting the view's extent with SetExtent in most cases except for scrolling views.
  125.  
  126. When overriding AdjustToNewLayout you decide the new location and size of the view inside its parent's content and then call SetLocation and SetSize.  If the view is not scrolling and contains subviews SetSize will propagate the   layout changes to the subviews through their own AdjustToNewLayout methods.
  127.  
  128. The Draw, Container, and Form samples show examples of custom layouts.  In Form the content view is centered when the frame is bigger and anchored to the top left corner when the frame is smaller:
  129.  
  130.   void CFormView::AdjustToNewLayout(Environment *ev, const FW_CPoint& oldExtent, 
  131.                                                         const FW_CPoint& newExtent, FW_Boolean redraw)    
  132.   {
  133.      FW_UNUSED(oldExtent);
  134.  
  135.         // Adjust the new frame size to remove scroll-bar space
  136.         FW_CPoint sbSize = FW_CScrollBar::GetDefaultScrollBarSize();
  137.         FW_CPoint size = newExtent;
  138.         size.x -= sbSize.x;
  139.         size.y -= sbSize.y;
  140.     
  141.         CenterInFrame(ev, size, redraw);
  142.     
  143.         return;
  144.   }
  145.  
  146. Example with Sibling Views
  147.  
  148. As mentioned in the previous section ODF view binding flags are not enough to handle the layout of sibling views relative to each other.  In the following example a parent view contains 3 subviews.  Views 1 and 2 keep a fixed height and an equal width when the parent's content grows, their method AdjustToNewLayout must be customized (*).  On the other hand view 3 keeps a fixed location and grows to follow the parent's content:  set its bindings to FW_kFitToEnclosure to implement this behavior.
  149.  
  150. (*)  It may seem that using FW_kLeftBinding for view 1 and FW_kRightBinding for view 2 is enough to do the job horizontally, since this will keep their width equal.  But there is a problem with that approach, the separation between the views won't be fixed anymore , in fact the views will overlap when the content grows too big.
  151.  
  152.  
  153.  
  154.  
  155.  
  156.  
  157.  
  158.  
  159.  
  160.  
  161.  
  162.  
  163.  
  164.  
  165.  
  166. The following listings show an implementation for the view's  AdjustToNewLayout methods.  The calculations are different but the it could be combined into one method if the views belong to the same class.  Also, the code shows what can be done to handle "limit conditions", i.e. when views becomes too small or too big.  You can also control this somewhat with the AdjustZoomedWindowSize and AdjustWindowGrowLimits as explained in the next section.
  167.  
  168.      // Adjust size of view 1
  169.   void CMyView1::AdjustToNewLayout(Environment *ev, const FW_CPoint& oldExtent, 
  170.                                                         const FW_CPoint& newExtent, FW_Boolean refresh)    
  171.   {
  172.      FW_UNUSED(oldExtent);
  173.  
  174.      // View 1's location & height don't change. 
  175.      // Its new width is half the parent's extent minus predefined margins
  176.  
  177.         FW_CPoint size = GetSize(ev);
  178.         size.x = newExtent.x / 2 - kLeftMargin - kHalfCenterMargin;
  179.     
  180.         if (size.x < kMinimumWidth)
  181.      {
  182.              // Makes view invisible when it gets too small
  183.           SetVisible(ev, false, true);
  184.      }
  185.      else
  186.      {        
  187.          if (IsVisible(ev) == false)
  188.               SetVisible(ev, true, true);   // Makes view visible again
  189.  
  190.             SetSize(ev, size, refresh);
  191.         }
  192.         return;
  193.   }
  194.  
  195.      // Adjust size of view 2
  196.   void CMyView2::AdjustToNewLayout(Environment *ev, const FW_CPoint& oldExtent, 
  197.                                                         const FW_CPoint& newExtent, FW_Boolean refresh)    
  198.   {
  199.      FW_UNUSED(oldExtent);
  200.  
  201.      // View 2's height doesn't change. 
  202.      // Its new width is half the parent's extent minus predefined margins
  203.      // Its new location is based on its width
  204.  
  205.         FW_CPoint size = GetSize(ev);
  206.         FW_CPoint loc = GetLocation(ev);
  207.         size.x = newExtent.x / 2 - kRightMargin - kHalfCenterMargin;
  208.      loc.x = newExtent.y - size.x - kRightMargin;
  209.     
  210.         SetLocation(ev, loc, refresh);
  211.  
  212.         if (size.x < kMinimumWidth)
  213.      {
  214.              // Makes view invisible when it gets too small
  215.           SetVisible(ev, false, true);
  216.      }
  217.      else
  218.      {        
  219.          if (IsVisible(ev) == false)
  220.               SetVisible(ev, true, true);   // Makes view visible again
  221.  
  222.             SetSize(ev, size, refresh);
  223.         }
  224.     
  225.         return;
  226.   }
  227.  
  228.  
  229. Controling Maximum and Minimum Sizes
  230.  
  231. Warning: an embedded OpenDoc frame can have its size enlarged or reduced to any  value by its container part.  This means that you cannot assume anything on the limits of your frame's size, and thus you cannot assume that its views won't be adjusted to any arbitrary small or large value; in theory the embedded frame can negociate its size once but it cannot do anything if the container is stubborn!  (However this doesn't apply to subviews belonging to a scrolling content view, since the content size is not controlled by the container part).
  232.  
  233. The consequence is that you should handle "limit conditions" in your code, especially for very small views.  In the previous example views are made invisible instead of getting a small or negative size.
  234.  
  235. ODF provides you with one small relief, an API to set the limits on your window's grow and zoom sizes.  This helps control the maximum and minimum sizes of root frames.  All you need to do is implement the following virtual methods in your frame class and update the arguments passed as reference:
  236.  
  237. void FW_CFrame::AdjustZoomedWindowSize(Environment *ev, FW_CPoint& proposedSize)
  238.  
  239. void FW_CFrame::AdjustWindowGrowLimits(Environment* ev, FW_CPoint& minSize, FW_CPoint& maxSize)
  240.  
  241.  
  242. Layout of Views in Resources
  243.  
  244. A frame or a superview can be defined in a resource file with a type FW_RFrameLayout or FW_RViewLayout.  The first resource field is a point representing the layout size followed by the list of subviews.  For instance Form's Views.fr  contains this:
  245.  
  246.   // H & V are reference size for the Form frame.
  247.   #define H         FW_FIX(600)
  248.   #define V         FW_FIX(1000)
  249.  
  250.   #define H1     H + FW_FIX(1)
  251.   #define V1     V + FW_FIX(1)
  252.  
  253.   resource FW_RFrameLayout(kFormView)
  254.   {
  255.        {H,V},                                            // LayoutSize (i.e. virtual extent)
  256.        {                                                    // Start list of frame's subviews
  257.               RFormView                    // Form's content view
  258.               (
  259.                      kFormViewID,                     // view id
  260.                      {0, 0, H, V},                    // bounds (changed at runtime)
  261.                      FW_kFitToEnclosure,           // bindings
  262.                      {FW_FIX(560),FW_FIX(800)},    // extent
  263.                      1,                                     // Make it the ContentView
  264.                      FW_kXYScrolling,                 // scrolling direction
  265.  
  266.                      {                                      // Start list of content's subviews
  267.                             FW_REditView            // First edit view
  268.                             (
  269.                                kFirstNameEdViewID,                
  270.                               { FW_FIX(290),FW_FIX(90),FW_FIX(450),FW_FIX(107)},
  271.                   ...
  272.                 )
  273.                 ...                    // more subviews
  274.             } 
  275.         )
  276.               FW_RScrollBar                      // Horizontal SB
  277.               (     
  278.                      kHorzScrollBarID,                // view id
  279.                      {-FW_ONE, V - FW_SBSIZE, H1 - FW_SBSIZE, V1},    //  SB bounds 
  280.             FW_kHScrollBarBinding,
  281.                   ...
  282.               ),
  283.               FW_RScrollBar                      // Vertical SB
  284.               (     ...
  285.               ),
  286.               FW_RGrowBox
  287.               (     ...
  288.               )
  289.         },                                            // End list of frame's subviews
  290.      ...
  291.   };
  292.  
  293. The layout size {H,V} is like a virtual extent (not the actual extent of the frame created from this resource!).  Its  values are not important, they are only used as a reference for the subviews defined below.  For instance the scroll bars and grow box are placed at the edges of this layout, with the correct 1 pixel overlap.   When the resource is loaded at runtime the layout size is replaced by the actual extent of the frame which has the effect of adjusting each subview's position and size according to their bindings.
  294.  
  295. In the example above RFormView is a scrolling content view.  Because its layout is customized by program the bounds {0,0,H,V} declared here don't have any significance, they will be reset after the view is created.  On the other hand the extent field defines the size of the content itself, it is independent of the view's bounds or the frame's layout.  Below, each subview inside the content, such as the edit view shown here, has its bounds defined in content coordinates.  They are not affected by the frame size either.
  296.  
  297.  
  298. API Reference
  299.  
  300. virtual void    SetSize(Environment* ev, const FW_CPoint& size, 
  301.                                        FW_Boolean refresh = TRUE); 
  302.  
  303. SetSize modifies the visible bounds of a view inside its parent's content.  If refresh is true it updates the affected regions (either the whole area oldSize + newSize if the view's ForceResizeRedraw flag is off, or only the region that changed if the flag is on).  See also SetForceResizeRedraw.
  304.  
  305. For a non-scrolling superview  SetSize also calls SetExtent to keep its extent similar to its size.  This in turn adjusts the layout of its subviews.
  306.  
  307. You should never call SetSize directly on instances of FW_CFrame.  Use FW_MProxy::ChangeFrameShapes to control the size of your frame.
  308.  
  309. SetSize should not be overriden by your view classes.  Override instead the SizeChanged virtual method called by SetSize.
  310.  
  311. virtual void    SetLocation(Environment* ev, const FW_CPoint& loc, 
  312.                                               FW_Boolean refresh = TRUE); 
  313.  
  314. SetLocation moves the visible bounds of a view inside its parent's content.  If refresh is true it updates the affected regions.  SetLocation called on a superview doesn't affect the layout of its subviews, it doesn't modify the superview's extent.
  315.  
  316. You are not allowed to call SetLocation directly on instances of FW_CFrame.  A frame's location is always 0,0 since it is the root view of its own views hierarchy.  Use other methods to move an embedded frame inside your part.
  317.  
  318. SetLocation should not be overriden by your view classes.  Override instead the LocationChanged virtual method called by SetLocation.
  319.  
  320. virtual void    SizeChanged(Environment* ev, const FW_CPoint& oldSize);
  321.  
  322. This virtual method is empty by default and called by SetSize with the old view size after it has been changed (the new size is simply returned by GetSize).  You can override it for any purpose.  However it is not intended to let you managethe layout of your views, override AdjustToNewLayout instead.
  323.  
  324. virtual void    LocationChanged(Environment* ev, const FW_CPoint& oldLoc);
  325.  
  326. This virtual method is empty by default and called by SetLocation with the old view location after it has been changed (the new location is simply returned by GetLocation).  You can override it for any purpose.  However it is not intended to let you managethe layout of your views, override AdjustToNewLayout instead.
  327.  
  328. virtual void    SetExtent(Environment* ev, const FW_CPoint& extent);
  329.  
  330. SetExtent  is a method of FW_CSuperView  (simple views don't have any extent).  It modifies a superview's extent and adjust  its subviews by calling their AdjustToNewLayout methods.  For the frame's content view SetExtent also updates the ODFrame's extent and adjust the scroller.
  331.  
  332. In general you don't need to call SetExtent in your program.  Frames are created with an extent equal to their size by default.  The superview's extent can be declared in the constructor or resource field, otherwise it is also set to its size.
  333.  
  334. virtual void    AdjustToNewLayout(Environment *ev,  
  335.                                                             const FW_CPoint& oldExtent, 
  336.                                                             const FW_CPoint& newExtent, 
  337.                                                             FW_Boolean refresh);
  338.  
  339. oldExtent = previous extent of the parent view.
  340. oldExtent = new extent of the parent view, to which the view must adjust to.
  341. refresh = true if the modified regions should be updated.
  342.  
  343. This method is called by the parent view's SetExtent method when the extent changes.   See sections above for more information.
  344.  
  345. void    SetResizeForceRedraw(Environment* ev, FW_Boolean refresh);
  346.  
  347. This method lets you change the fResizeForceRedraw flag in FW_CView objects.  When this flag is true the entire view is refreshed if its size changed, when it is false only the modified regions are refreshed.  By default FW_CView objects are created with fResizeForceRedraw set to true and FW_CSuperView objects have it set to false: in general the drawing of a simple view's content depends on its size, but not the drawing of a superview's content (in this case the subviews don't count because the superview's content is updated automatically when their layout is adjusted).  You must inverse the flag if this behavior is not rigtht for you.
  348.  
  349. Notes:
  350. • SincefResizeForceRedraw is not a view resource field you must call SetResizeForceRedraw in your PostCreatViewFromStream method.
  351. • Another way of overriding this flag is to call Invalidate(ev) in your view's SizeChanged method.  Or for a frame to call Invalidate(ev) in its FrameShapeChanged method.
  352.  
  353. © 1993 - 1996 Apple Computer, Inc. All rights reserved.
  354. Apple, the Apple Logo, Macintosh, and OpenDoc are trademarks of Apple Computer, Inc., registered in the United States and other countries.